#include "XXthread.h"
#include "./XXlogger.h"
#include <QTimerEvent>
#include <QMutexLocker>
#include <QList>
#include <QSemaphoreReleaser>
#include <QDateTime>
#include <mutex>
#include <QMutexLocker>

XXthread::XXthread(QObject *parent) : QThread (parent){
    qRegisterMetaType<std::function<void(void)>>("std::function<void(void)>");
    _delayTimerID = -1;
    _delayDuration = 0;
    _finishBlock = nullptr;
    _stepMode = false;
    _taskSemaphore = nullptr;
    connect(this,&XXthread::finished,this,&XXthread::onFinished);
    connect(this,&XXthread::sigMainThread,this,&XXthread::onMainThread,Qt::QueuedConnection);
}
XXthread::~XXthread(){
    requestInterruption();
    wait();
    terminate();
    if(_taskSemaphore != nullptr){
        delete _taskSemaphore;
    }
}

void XXthread::addTask(std::function<void(void)> block, bool mainThread, QString name){
    if(name.isEmpty()){
        name = QString::number(QDateTime::currentDateTime().toMSecsSinceEpoch());
    }
    XXthreadTask action;
    action.name = name;
    action.mainThread = mainThread;
    action.block = block;
    addTask(action);
}
void XXthread::setFinish(std::function<void(void)> block){
    _finishBlock = block;
}

void XXthread::nextStep(){
    const QSemaphoreReleaser releaser(_taskSemaphore);
}

void XXthread::setStepMode(bool enable){
    _stepMode = enable;
}
void XXthread::setDelay(int ms){
    _delayDuration = ms;
}
void XXthread::start(){
    if(isRunning()){
        xxLog("could not start again.");
        return;
    }
    if(taskCount()<=0){
        xxLog("could not start when 'workBlockList' is empty.");
        return;
    }
    if(_taskSemaphore != nullptr){
        delete _taskSemaphore;
    }
    _taskSemaphore = new QSemaphore(0);
    if(_delayDuration>0){
        startDelayTimer();
    }
    else{
        QThread::start();
    }
}
void XXthread::requestInterruption(){
    QThread::requestInterruption();
    killDelayTimer();
    removeAllTask();
    const QSemaphoreReleaser releaser(_taskSemaphore);
}


void XXthread::startDelayTimer(){
    killDelayTimer();
    _delayTimerID = startTimer(_delayDuration);
}
void XXthread::killDelayTimer(){
    if(_delayTimerID>=0){
        killTimer(_delayTimerID);
        _delayTimerID=-1;
    }
}
void XXthread::run(){
    while(!isInterruptionRequested() && taskCount()>0){
        auto action = takeFirstTask();
        if(action.mainThread){
            emit sigMainThread(action.block);
        }
        else{
            action.block();
        }
        if(_stepMode && !isInterruptionRequested() && taskCount()!=0){
            _taskSemaphore->acquire(1);
        }
    }
}
void XXthread::timerEvent(QTimerEvent *event){
    if(event->timerId()==_delayTimerID){
        killDelayTimer();
        if(isInterruptionRequested()){
            return;
        }
        QThread::start();
    }
}
void XXthread::onFinished(){
    if(isInterruptionRequested()){
        return;
    }
    if(_finishBlock){
        _finishBlock();
    }
}

void XXthread::addTask(const XXthreadTask &action){
    QMutexLocker locker(&_tasksMutex);
    _tasks << action;
}
XXthreadTask XXthread::takeFirstTask(){
    QMutexLocker locker(&_tasksMutex);
    return _tasks.takeFirst();
}
void XXthread::removeAllTask(){
    QMutexLocker locker(&_tasksMutex);
    _tasks.clear();
}
int XXthread::taskCount(){
    QMutexLocker locker(&_tasksMutex);
    return _tasks.count();
}
void XXthread::onMainThread(std::function<void(void)> block){
    if(isInterruptionRequested()){
        return;
    }
    block();
}

XXthreadDispatcher* XXthreadDispatcher::_instance = nullptr;
XXthreadDispatcher* XXthreadDispatcher::instance(){
    static std::once_flag once;
    std::call_once(once,[&]{
        if(nullptr==_instance){
            _instance = new XXthreadDispatcher;
        }
    });
    return _instance;
}

QSharedPointer<XXthread> XXthreadDispatcher::create(const QString &name){
    QMutexLocker locker(&_nameToThreadMutex);
    if(_nameToThread.contains(name)){
        xxErrStr(QString("xxthread is exists, could not create again. name:%1").arg(name));
        return nullptr;
    }
    QSharedPointer<XXthread> thread;
    thread.reset(new XXthread);
    _nameToThread[name] = thread;
    return thread;
}
QSharedPointer<XXthread> XXthreadDispatcher::get(const QString &name){
    QMutexLocker locker(&_nameToThreadMutex);
    return _nameToThread.value(name,nullptr);
}
void XXthreadDispatcher::remove(const QString &name){
    QMutexLocker locker(&_nameToThreadMutex);
    auto thread = _nameToThread.take(name);
    if(thread!=nullptr){
        emit sigDeleteLater(thread);
    }
}
